local Class = require('lua.lang.Class')

--- @class Double : Class
--- @field value number
local Double = Class {}

--- @param d number
--- @return Double
function Double:new(d)
	--- @type Double
	local o = {}
	o.value = d
	return setmetatable(o, Double)
end

--- @param d number
--- @return number
function Double:doubleToLongBits(d)
	if d ~= d then
		return 0x7FF8000000000000
	end
	if d == 0.0 then
		if 1 / d < 0 then
			return 0x8000000000000000
		else
			return 0x0000000000000000
		end
	end
	if d == math.huge then
		return 0x7FF0000000000000
	elseif d == -math.huge then
		return 0xFFF0000000000000
	end
	local sign = d < 0 and 0x8000000000000000 or 0x0000000000000000
	d = math.abs(d)
	local m, e = math.frexp(d)
	m = m * 2.0
	e = e - 1
	local biasedExp = e + 1023
	if biasedExp > 0 and biasedExp < 0x7FF then
		local fraction = (m - 1.0) * 4503599627370496.0  -- 2^52
		fraction = math.floor(fraction + 0.5)
		if fraction >= 4503599627370496.0 then
			fraction = 0
			biasedExp = biasedExp + 1
			if biasedExp >= 0x7FF then
				return sign | 0x7FF0000000000000
			end
		end
		return sign | ((0 + biasedExp) * 0x10000000000000) | (fraction + 0)
	end
	local scale = 2.0 ^ 1074
	local fraction = d * scale
	fraction = math.floor(fraction + 0.5)
	if fraction >= 0x10000000000000 then
		-- 2^52
		fraction = 0
		biasedExp = 1
		return sign | (biasedExp * 0x10000000000000)
	end
	return sign | fraction
end

--- @param bits number
--- @return number
function Double:longBitsToDouble(bits)
	if bits == 0x7FF8000000000000 then
		return 0 / 0  -- NaN
	end
	if bits == 0x0000000000000000 then
		return 0.0
	elseif bits == 0x8000000000000000 then
		return -0.0
	end
	if bits == 0x7FF0000000000000 then
		return math.huge
	elseif bits == 0xFFF0000000000000 then
		return -math.huge
	end
	local signBit = (bits & 0x8000000000000000) ~= 0
	local exponentBits = (bits >> 52) & 0x7FF
	local mantissaBits = bits & 0xFFFFFFFFFFFFF
	local sign = signBit and -1 or 1
	if exponentBits > 0 and exponentBits < 0x7FF then
		local exponent = exponentBits - 1023
		local mantissa = 1.0 + mantissaBits / 4503599627370496.0  -- 2^52
		return sign * math.ldexp(mantissa, exponent)
	end
	if exponentBits == 0 then
		local exponent = -1022
		local mantissa = mantissaBits / 4503599627370496.0
		return sign * math.ldexp(mantissa, exponent)
	end
	return 0 / 0
end

return Double